<?php

namespace Yolo\Components\Gateway;

use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Exception\HttpException;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Swow\Psr7\Message\ResponsePlusInterface;
use Throwable;
use Yolo\Errors\BadRequestException;
use Yolo\Errors\ForbiddenException;
use Yolo\Errors\MethodNotAllowedException;
use Yolo\Errors\NotFoundException;
use Yolo\Errors\RpcException;
use Yolo\Errors\SystemErrorException;
use Yolo\Errors\UnauthorizedException;

class HttpExceptionHandler extends ExceptionHandler
{

    /**
     * Handle the exception, and return the specified result.
     */
    public function handle(Throwable $throwable, ResponsePlusInterface $response)
    {
        if ($throwable instanceof HttpException) {

            $this->stopPropagation(); // 停止传播异常
            return $this->createResponse($throwable, $response);
        } elseif ($throwable instanceof RpcException) {

            logError([
                'action' => 'request',
                'message' => $throwable->getMessage(),
                'data' => [
                    'details' => $throwable->json(),
                ],
            ]);

            $this->stopPropagation(); // 停止传播异常
            $response->setStatus($throwable->getStatusCode());
            $response->setBody(new SwooleStream(json_encode($throwable->json())));
            return $response;
        }

        // 继续处理其他类型的异常
        return $response;
    }/**
 * Determine if the current exception handler should handle the exception.
 *
 * @return bool If return true, then this exception handler will handle the exception and then call the next handler,
 *              If return false, this handler will be ignored and the next will be called
 * @see ExceptionHandler::stopPropagation() if you want to stop propagation after handling
 * an exception, as returning `true` in `isValid` does not stop the handlers call loop.
 *
 */
    public function isValid(Throwable $throwable): bool
    {
        return true;
    }

    private function createResponse(HttpException $exception, ResponsePlusInterface $response): ResponsePlusInterface
    {
        $statusCode = $exception->getStatusCode();
        $response->setStatus($statusCode);

        try {
            $ex = match ($statusCode) {
                400 => new BadRequestException(),
                401 => new UnauthorizedException(),
                403 => new ForbiddenException(),
                404 => new NotFoundException(),
                405 => new MethodNotAllowedException(),
                default => new SystemErrorException(),
            };
            $ex->setStatusCode($statusCode);
            throw $ex;
        } catch (RpcException $throwable) {

            logError([
                'action' => 'request',
                'message' => $throwable->getMessage(),
                'data' => [
                    'details' => $throwable->json(),
                ],
            ]);

            $response->setBody(new SwooleStream(json_encode($throwable->json())));
        } catch (Throwable $throwable) {

            logError([
                'action' => 'request',
                'message' => $throwable->getMessage(),
                'data' => [
                    'http' => [
                        'status' => $statusCode,
                    ]
                ],
            ]);

            $response->setBody(new SwooleStream(json_encode([
                'code' => -1,
                'symbol' => 'system_error',
                'message' => $throwable->getMessage() ?: 'Server Internal Error.',
                'metadata' => [],
                'http' => [
                    'status' => $statusCode,
                ]
            ])));
        }

        return $response;
    }
}